﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace WorldBankBBS.Logic
{
    public class BbsDispatcher
    {
        private static Dictionary<Session, BbsDispatcher> _dispatchers = new Dictionary<Session, BbsDispatcher>();

        private Dictionary<string, EventHandler<Session>> _invokers = new Dictionary<string, EventHandler<Session>>();
        private Dictionary<string, object> _objects = new Dictionary<string, object>();
        //private static BbsDispatcher _instance = new BbsDispatcher();

        private BbsDispatcher()
        {}

        //public static BbsDispatcher Instance
        //{
        //    get { return _instance; }
        //    set { _instance = value; }
        //}

        public static BbsDispatcher GetDispatcher(Session session)
        {
            if (!_dispatchers.ContainsKey(session))
            {
                _dispatchers.Add(session, new BbsDispatcher());
            }

            return _dispatchers[session];
        }

        public T GetObject<T>(string name) where T: class
        {
            return _objects.ContainsKey(name) ? _objects[name] as T : null;
        }

        public void ApplyHandlers(Session session)
        {
            if (_invokers.Count > 0) return;

            _objects.Add(session.GetType().FullName, session);

            foreach (var key in ConfigurationManager.AppSettings.Keys)
            {
                if (key.ToString().StartsWith("WorldBankBBS.") || 
                    key.ToString().StartsWith("WorldBankBBS:"))
                {
                    var length = key.ToString().LastIndexOf('.');
                    var colon = key.ToString().IndexOf(':');
                    var assemblyName = key.ToString().Substring(0, colon);
                    var invokerName = key.ToString().Substring(colon + 1, length - colon - 1);
                    var type = Type.GetType(invokerName + ", " + assemblyName);
                    if (type != null)
                    {
                        var o = _objects.ContainsKey(invokerName) ? _objects[invokerName] : Activator.CreateInstance(type, new [] { session });
                        var n = key.ToString().Substring(length + 1);
                        var e = type.GetEvent(n);

                        if (e != null)
                        {
                            var value = ConfigurationManager.AppSettings[key.ToString()];
                            var targetLength = value.LastIndexOf('.');
                            var targetColon = value.IndexOf(':');
                            var targetAssemblyName = value.Substring(0, targetColon);
                            var targetName = value.Substring(targetColon + 1, targetLength - targetColon - 1);
                            var targetType = Type.GetType(targetName + ", " + targetAssemblyName);

                            if (targetType != null)
                            {
                                object t = null;

                                try
                                {
                                    t = Activator.CreateInstance(targetType, new[] { session });
                                }
                                catch (MissingMethodException)
                                {
                                    try
                                    {
                                        t = Activator.CreateInstance(targetType);
                                    }
                                    catch (MissingMethodException)
                                    {
                                    }
                                }

                                if (t != null)
                                {
                                    var h = t.GetType()
                                        .GetMethod(value.Substring(targetLength + 1),
                                            new[] {typeof (object), typeof (Session)});

                                    if (h != null)
                                    {
                                        var d = Delegate.CreateDelegate(e.EventHandlerType, h);
                                        e.AddEventHandler(o, d);
                                        _invokers[key.ToString()] = d as EventHandler<Session>;

                                        //if (invokerName == "WorldBankBBS.Logic.BbsMain") bbs = (BbsMain)o;
                                        _objects[invokerName] = o;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        public EventHandler<Session> GetInvoker(string invokerName)
        {
            return _invokers[invokerName];
        }
    }
}
